﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using SHomeWorkshop.LunarConcept.Controls;
using System.Xml;
using SHomeWorkshop.LunarConcept.ModifingManager;

namespace SHomeWorkshop.LunarConcept.Dialogs
{
    /// <summary>
    /// ReplaceDialog.xaml 的交互逻辑
    /// </summary>
    public partial class ReplaceDialog : Window
    {
        public ReplaceDialog(Window ownerWindow, EditorManager masterEditorManager)
        {
            InitializeComponent();

            this.Owner = ownerWindow;
            this.masterEditorManager = masterEditorManager;
        }

        private EditorManager masterEditorManager;
        /// <summary>
        /// 本替换对话框的作用范围最大只限定于此页面管理器的所有页面。
        /// 应在本替换对话框的构造方法中传入一个页面管理器实例对象。
        /// </summary>
        protected EditorManager MasterEditorManager
        {
            get { return masterEditorManager; }
        }

        private void BtnReplaceOnAllPages_Click(object sender, RoutedEventArgs e)
        {
            if (masterEditorManager == null)
            {
                MessageBox.Show("　　构造方法中未传入页面管理器，这是个程序错误。无法执行替换操作。", Globals.AppName,
                    MessageBoxButton.OK, MessageBoxImage.Warning);
                return;
            }

            string oldText = CmbFindText.Text;
            string newText = CmbReplaceText.Text;

            if (oldText == string.Empty)
            {
                MessageBox.Show("　　要查找的文本不能为空文本。无法执行替换操作。", Globals.AppName,
                    MessageBoxButton.OK, MessageBoxImage.Warning);
                return;
            }

            if (oldText == newText)
            {
                MessageBox.Show("　　要替换掉文本与提供的新文本相同。替换操作无意义。", Globals.AppName,
                    MessageBoxButton.OK, MessageBoxImage.Warning);
                return;
            }

            List<PageEditor> selectedPages = masterEditorManager.GetAllPageEditors();
            if (selectedPages.Count <= 0)
            {
                MessageBox.Show("　　当前文档目前没有任何页面。无法执行替换操作。", Globals.AppName,
                    MessageBoxButton.OK, MessageBoxImage.Warning);
                return;
            }

            ModifingInfo info = new ModifingInfo();
            info.ModifingDescription = "替换文本";
            masterEditorManager.GetSelectedPageEditorStatus(info);
            masterEditorManager.GetSelectedWidgetStatus_Old(info);
            masterEditorManager.GetSelectedWidgetStatus_New(info);

            ModifingItem<Action, ModifingInfo> mi = new ModifingItem<Action, ModifingInfo>(info);


            foreach (PageEditor pe in selectedPages)
            {
                foreach (Widgets.Widget w in pe.Children)
                {
                    if (w.XmlData == null) continue;

                    XmlNode parasSetNode = w.XmlData.SelectSingleNode(XmlTags.ParagraphSetTag);
                    if (parasSetNode == null) continue;

                    XmlNodeList paraNodeList = parasSetNode.SelectNodes(XmlTags.ParagraphTag);
                    if (paraNodeList == null || paraNodeList.Count <= 0) continue;

                    bool needReplace = false;
                    foreach (XmlNode paraNode in paraNodeList)
                    {
                        if (paraNode.InnerText.Contains(oldText)) { needReplace = true; break; }
                    }

                    if (needReplace == false) continue;
                    string oldXml = parasSetNode.InnerXml;
                    //StringBuilder sb = new StringBuilder();

                    //foreach (XmlNode paraNode in paraNodeList)
                    //{
                    //    sb.Append("<" + XmlTags.ParagraphTag + "><" + XmlTags.TextTag + ">" +
                    //        paraNode.InnerText.Replace(oldText, newText) +
                    //        "</" + XmlTags.TextTag + "></" + XmlTags.ParagraphTag + ">");
                    //}

                    //parasSetNode.InnerXml = sb.ToString();

                    ReplaceInXmlParagraph(parasSetNode, oldText, newText);

                    Action actReplace = new Action(pe.Id, w.Id, w.GetType().Name, XmlTags.XmlDataInnerXml, oldXml, parasSetNode.InnerXml);

                    w.Build();

                    mi.AddAction(actReplace);
                }
            }

            masterEditorManager.RegisterModifingItem(mi);

            this.AddKeywords();
        }

        private void BtnReplaceOnSelectedPages_Click(object sender, RoutedEventArgs e)
        {
            if (masterEditorManager == null)
            {
                MessageBox.Show("　　构造方法中未传入页面管理器，这是个程序错误。无法执行替换操作。", Globals.AppName,
                    MessageBoxButton.OK, MessageBoxImage.Warning);
                return;
            }

            string oldText = CmbFindText.Text;
            string newText = CmbReplaceText.Text;

            if (oldText == string.Empty)
            {
                MessageBox.Show("　　要查找的文本不能为空文本。无法执行替换操作。", Globals.AppName,
                    MessageBoxButton.OK, MessageBoxImage.Warning);
                return;
            }

            if (oldText == newText)
            {
                MessageBox.Show("　　要替换掉文本与提供的新文本相同。替换操作无意义。", Globals.AppName,
                    MessageBoxButton.OK, MessageBoxImage.Warning);
                return;
            }

            List<PageEditor> selectedPages = masterEditorManager.GetSelectedPageEditorsList();
            if (selectedPages.Count <= 0)
            {
                MessageBox.Show("　　目前没有选定任何页面。无法执行替换操作。", Globals.AppName,
                    MessageBoxButton.OK, MessageBoxImage.Warning);
                return;
            }

            ModifingInfo info = new ModifingInfo();
            info.ModifingDescription = "替换文本";
            masterEditorManager.GetSelectedPageEditorStatus(info);
            masterEditorManager.GetSelectedWidgetStatus_Old(info);
            masterEditorManager.GetSelectedWidgetStatus_New(info);

            ModifingItem<Action, ModifingInfo> mi = new ModifingItem<Action, ModifingInfo>(info);


            foreach (PageEditor pe in selectedPages)
            {
                foreach (Widgets.Widget w in pe.Children)
                {
                    if (w.XmlData == null) continue;

                    XmlNode parasSetNode = w.XmlData.SelectSingleNode(XmlTags.ParagraphSetTag);
                    if (parasSetNode == null) continue;

                    XmlNodeList paraNodeList = parasSetNode.SelectNodes(XmlTags.ParagraphTag);
                    if (paraNodeList == null || paraNodeList.Count <= 0) continue;

                    bool needReplace = false;
                    foreach (XmlNode paraNode in paraNodeList)
                    {
                        if (paraNode.InnerText.Contains(oldText)) { needReplace = true; break; }
                    }

                    if (needReplace == false) continue;
                    string oldXml = parasSetNode.InnerXml;
                    //StringBuilder sb = new StringBuilder();

                    //foreach (XmlNode paraNode in paraNodeList)
                    //{
                    //    sb.Append("<" + XmlTags.ParagraphTag + "><" + XmlTags.TextTag + ">" +
                    //        paraNode.InnerText.Replace(oldText, newText) +
                    //        "</" + XmlTags.TextTag + "></" + XmlTags.ParagraphTag + ">");
                    //}

                    //parasSetNode.InnerXml = sb.ToString();

                    ReplaceInXmlParagraph(parasSetNode, oldText, newText);

                    Action actReplace = new Action(pe.Id, w.Id, w.GetType().Name, XmlTags.XmlDataInnerXml, oldXml, parasSetNode.InnerXml);

                    w.Build();

                    mi.AddAction(actReplace);
                }
            }

            masterEditorManager.RegisterModifingItem(mi);

            this.AddKeywords();
        }

        private void BtnReplaceOnSelectedWidgets_Click(object sender, RoutedEventArgs e)
        {
            if (masterEditorManager == null)
            {
                MessageBox.Show("　　构造方法中未传入页面管理器，这是个程序错误。无法执行替换操作。", Globals.AppName,
                    MessageBoxButton.OK, MessageBoxImage.Warning);
                return;
            }

            string oldText = CmbFindText.Text;
            string newText = CmbReplaceText.Text;

            if (oldText == string.Empty)
            {
                MessageBox.Show("　　要查找的文本不能为空文本。无法执行替换操作。", Globals.AppName,
                    MessageBoxButton.OK, MessageBoxImage.Warning);
                return;
            }

            if (oldText == newText)
            {
                MessageBox.Show("　　要替换掉文本与提供的新文本相同。替换操作无意义。", Globals.AppName,
                    MessageBoxButton.OK, MessageBoxImage.Warning);
                return;
            }

            PageEditor mainSelectedPage = masterEditorManager.GetMainSelectedPageEditor();
            if (mainSelectedPage == null)
            {
                MessageBox.Show("　　目前没有选定任何页面。无法执行替换操作。", Globals.AppName,
                    MessageBoxButton.OK, MessageBoxImage.Warning);
                return;
            }

            List<Widgets.Widget> selectedWidgets = mainSelectedPage.GetSelectedWidgetsList();
            if (selectedWidgets == null || selectedWidgets.Count <= 0)
            {
                MessageBox.Show("　　目前没有选定任何部件。无法执行此替换操作。", Globals.AppName,
                    MessageBoxButton.OK, MessageBoxImage.Warning);
                return;
            }

            ModifingInfo info = new ModifingInfo();
            info.ModifingDescription = "替换文本";
            masterEditorManager.GetSelectedPageEditorStatus(info);
            masterEditorManager.GetSelectedWidgetStatus_Old(info);
            masterEditorManager.GetSelectedWidgetStatus_New(info);

            ModifingItem<Action, ModifingInfo> mi = new ModifingItem<Action, ModifingInfo>(info);

            foreach (Widgets.Widget w in selectedWidgets)
            {
                if (w.XmlData == null) continue;

                XmlNode parasSetNode = w.XmlData.SelectSingleNode(XmlTags.ParagraphSetTag);
                if (parasSetNode == null) continue;

                XmlNodeList paraNodeList = parasSetNode.SelectNodes(XmlTags.ParagraphTag);
                if (paraNodeList == null || paraNodeList.Count <= 0) continue;

                bool needReplace = false;
                foreach (XmlNode paraNode in paraNodeList)
                {
                    if (paraNode.InnerText.Contains(oldText)) { needReplace = true; break; }
                }

                if (needReplace == false) continue;
                string oldXml = parasSetNode.InnerXml;
                //StringBuilder sb = new StringBuilder();

                //foreach (XmlNode paraNode in paraNodeList)
                //{
                //    sb.Append("<" + XmlTags.ParagraphTag + "><" + XmlTags.TextTag + ">" +
                //        paraNode.InnerText.Replace(oldText, newText) +
                //        "</" + XmlTags.TextTag + "></" + XmlTags.ParagraphTag + ">");
                //}

                //parasSetNode.InnerXml = sb.ToString();

                ReplaceInXmlParagraph(parasSetNode, oldText, newText);

                Action actReplace = new Action(mainSelectedPage.Id, w.Id, w.GetType().Name, XmlTags.XmlDataInnerXml, oldXml, parasSetNode.InnerXml);

                w.Build();

                mi.AddAction(actReplace);
            }

            masterEditorManager.RegisterModifingItem(mi);

            this.AddKeywords();
        }

        private void AddKeywords()
        {
            if (CmbFindText.Items.Count >= 1 && CmbFindText.Items[0] as string != CmbFindText.Text)
            {
                CmbFindText.Items.Insert(0, CmbFindText.Text);
            }
            else
            {
                CmbFindText.Items.Insert(0, CmbFindText.Text);
            }

            if (CmbReplaceText.Items.Count >= 1 && CmbReplaceText.Items[0] as string != CmbReplaceText.Text)
            {
                CmbReplaceText.Items.Insert(0, CmbReplaceText.Text);
            }
            else
            {
                CmbReplaceText.Items.Insert(0, CmbReplaceText.Text);
            }
        }

        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            this.CmbFindText.Focus();
            Globals.SwitchInputMethod(true);
        }

        private void Window_Closed(object sender, EventArgs e)
        {
            Globals.SwitchInputMethod(false);
        }

        private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
        {
            this.Hide();
            e.Cancel = true;
        }

        private void Window_KeyUp(object sender, KeyEventArgs e)
        {
            if (e.Key == Key.Escape)
            {
                this.Hide();
            }
        }

        /// <summary>
        /// 在各段落中替换文本。不会替换跨段文本（不支持替换文本中包含换行、回车的情况）。
        /// 
        /// 异常：ArgumentNullException。
        /// </summary>
        /// <param name="paragraphSetNode">ParagraphSetNode，其中应包括一个或多个ParagraphNode。
        /// 一个ParagraphNode中则应包括一个或多个TextNode。</param>
        /// <param name="oldText">要替换掉的文本。不能为null或空字符串。</param>
        /// <param name="newText">不能为null，但可以为空字符串（此时为“替换删除”）。</param>
        private void ReplaceInXmlParagraph(XmlNode paragraphSetNode, string oldText, string newText)
        {
            if (paragraphSetNode == null)
                throw new ArgumentNullException("ReplaceInXmlParagraph方法的paragraphSetNode参数为能为null。");

            if (string.IsNullOrEmpty(oldText))
                throw new ArgumentNullException("ReplaceInXmlParagraph方法的oldText参数为能为null或空字符串。");

            if (newText == null)
                throw new ArgumentNullException("ReplaceInXmlParagraph方法的newText参数为能为null。");


            foreach (XmlNode paragraphNode in paragraphSetNode.ChildNodes)
            {
                XmlNodeList textNodeList = paragraphNode.ChildNodes;
                if (textNodeList.Count <= 0) continue;

                if (textNodeList.Count == 1)
                {
                    textNodeList[0].InnerText = textNodeList[0].InnerText.Replace(oldText, newText);
                    continue;
                }

                foreach (XmlNode textNode in textNodeList)
                {
                    textNode.InnerText = textNode.InnerText.Replace(oldText, newText);
                    //先解决掉“片内替换”的问题。
                }

                //再解决“跨片（TextNode）替换”的问题

                string wholeParagraphInnerText = paragraphNode.InnerText;//初步替换后。用于求偏移量。

                int lastIndexOfOldText = wholeParagraphInnerText.LastIndexOf(oldText);
                int oldTextStartIndex = lastIndexOfOldText;
                int oldTextEndIndex = oldTextStartIndex + oldText.Length;

                while (lastIndexOfOldText >= 0)
                {
                    XmlNode startTextNode = null;
                    XmlNode endTextNode = null;
                    int startTextNodeOffset = 0;//要替换的旧文本首端出现在的首TextNode中的偏移量。
                    int endTextNodeOffset = 0;//要替换的旧文本尾部出现在的尾TextNode中的偏移量。

                    int offset = 0;

                    List<XmlNode> willRemoveXmlNodeList = new List<XmlNode>();

                    foreach (XmlNode textNode in textNodeList)
                    {
                        if (textNode.InnerText.Length <= 0)
                        {
                            willRemoveXmlNodeList.Add(textNode);
                            continue;
                        }

                        int textNodeTextStartIndex = offset;
                        int textNodeTextEndIndex = offset + textNode.InnerText.Length;

                        if (oldTextStartIndex >= textNodeTextStartIndex && oldTextStartIndex < textNodeTextEndIndex)
                        {
                            startTextNode = textNode;//取出被替换的旧文本“落脚首端点”在哪个TextNode.
                            startTextNodeOffset = oldTextStartIndex - textNodeTextStartIndex;
                        }

                        if (textNodeTextStartIndex > oldTextStartIndex && textNodeTextEndIndex < oldTextEndIndex)
                        {
                            willRemoveXmlNodeList.Add(textNode);//在欲替换的旧文本中间的节点将被删除。
                        }

                        if (oldTextEndIndex > textNodeTextStartIndex && oldTextEndIndex <= textNodeTextEndIndex)
                        {
                            endTextNode = textNode;//取出被替换的旧文本“落脚尾端点”在哪个TextNode.
                            endTextNodeOffset = oldTextEndIndex - textNodeTextStartIndex;
                        }

                        offset += textNode.InnerText.Length;
                    }

                    //处理“跨片（TextNode）替换”
                    if (startTextNode != null && endTextNode != null)
                    {
                        if (startTextNode != endTextNode)
                        {
                            startTextNode.InnerText = startTextNode.InnerText.Substring(0, startTextNodeOffset) + newText;
                            //用于替换的新文本，直接追加到首节点的尾部（去除被替换的旧文本的一部分）

                            endTextNode.InnerText = endTextNode.InnerText.Substring(endTextNodeOffset);
                            //尾节点则直接去除前端属于被替换的旧文本的那部分字符。

                            if (endTextNode.InnerText.Length <= 0 && willRemoveXmlNodeList.Contains(endTextNode) == false)
                            {
                                paragraphNode.RemoveChild(endTextNode);//避免重复删除。
                            }
                        }
                        //相等没必要处理，事实上不可能出现。
                    }

                    //删除处于替换文本首尾索引之间的多余节点。
                    if (willRemoveXmlNodeList.Count > 0)
                    {
                        foreach (XmlNode node in willRemoveXmlNodeList)
                        {
                            if (node.ParentNode == paragraphNode)
                            {
                                paragraphNode.RemoveChild(node);
                            }
                        }
                    }

                    //继续倒找。
                    if (lastIndexOfOldText > 0)
                    {
                        lastIndexOfOldText = wholeParagraphInnerText.LastIndexOf(oldText, lastIndexOfOldText - 1);
                    }
                    else
                    {
                        lastIndexOfOldText = -1;
                    }
                }
            }
        }
    }
}
